#include <sys/socket.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

using namespace std;
int main(){

    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd==-1){
        cout<<"创建失败！"<<endl;
        exit(-1);
    }
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_addr.s_addr=INADDR_ANY;
    // inet_pton(AF_INET,"192.168.110.130",&saddr.sin_addr.s_addr);
    //主机字节序转换为网络字节序
    saddr.sin_port=htons(9990);

    //端口复用
    int optval=1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));

    int ret=bind(listenfd,(struct sockaddr*) &saddr,sizeof(saddr));
    if(ret==-1){
        cout<<"bind error"<<endl;
        exit(-1);
    }
    ret= listen(listenfd,8);
    if(ret==-1){
        cout<<"listen error"<<endl;
        exit(-1);
    }
    
    //创建一个fd_set的集合，存放的是需要检测的文件描述符
    fd_set rdset,tem;
    FD_ZERO(&rdset);
    FD_SET(listenfd,&rdset);
    int maxfd=listenfd;

    cout<<"server listening on port "<<saddr.sin_port<<"..."<<endl;

    while(true){
        tem=rdset;

        //调用select系统函数，让内核检测那些文件描述符有数据
        int rets=select(maxfd+1,&tem,NULL,NULL,NULL);
        if(rets==-1){
            perror("select error");
            exit(-1);
        }else if(rets==0){
            continue;
        }else if(rets>0){
            //说明检测到了文件描述符的对应缓冲区的数据发生了变化
            if(FD_ISSET(listenfd,&tem)){
                //表示有新的客户端连接
                //监听客户端连接
                struct sockaddr_in clientAddrs;
                socklen_t lenClient=sizeof(clientAddrs);
                int cfd=accept(listenfd,(struct sockaddr *) &clientAddrs, &lenClient);
                if(cfd==-1){
                    cout<<"accept error"<<endl;
                    exit(-1);
                }
                //输出客户端信息
                char clientIP[16];
                inet_ntop(AF_INET,&clientAddrs.sin_addr.s_addr,clientIP,sizeof(clientIP));
                //网络字节序转换为主机字节序
                unsigned short clientPort=ntohs(clientAddrs.sin_port);
                cout<<"client addr="<<clientIP<<"    client port="<<clientPort<<endl;
                //将新的文件描述符加入到集合中
                FD_SET(cfd,&rdset);
                maxfd=maxfd>cfd?maxfd:cfd;
            }

            //如果不是新连接，而是其它客户端的通信
            for(int i=listenfd+1;i<=maxfd;++i){
                if(FD_ISSET(i,&tem)){
                    //通信
                    char buf[1024];

                    int num=recv(i,buf,sizeof(buf),0);
                    if(num==-1){
                        cout<<"read error"<<endl;
                        exit(-1);
                    }else if(num==0){
                        cout<<"client closed...\n"<<endl;
                        close(i);
                        FD_CLR(i,&rdset);
                        
                    }else if(num>0){
                        cout<<"read data="<<buf<<endl;
                        const char* response="hello, from server";
                        send(i,response,strlen(response),0);
                    }
                   
                }
            }

        }
        
    }
    close(listenfd);

    return 0;
}